﻿#pragma once
#include "SmStepObject.h"
#include "SmQuestComponent.h"


USmStepObject::USmStepObject()
	:StepObjectState(ESmStepObjectState::Type::DefaultNone)
{
	// 照GA抄的，根据一些重要方法蓝图里是否重写，来初始化一些布尔值
	auto ImplementedInBlueprint = [](const UFunction* Func) -> bool
	{
		return Func && ensure(Func->GetOuter())
			&& (Func->GetOuter()->IsA(UBlueprintGeneratedClass::StaticClass()) || Func->GetOuter()->IsA(UDynamicClass::StaticClass()));
	};

	{
		static FName FuncName = FName(TEXT("K2_StepStart"));
		UFunction* ShouldRespondFunction = GetClass()->FindFunctionByName(FuncName);
		bHasBlueprintStepStart = ImplementedInBlueprint(ShouldRespondFunction);
	}
	{
		static FName FuncName = FName(TEXT("K2_StepEnd"));
		UFunction* CanActivateFunction = GetClass()->FindFunctionByName(FuncName);
		bHasBlueprintStepEnd = ImplementedInBlueprint(CanActivateFunction);
	}

	// 照GA抄的
#if WITH_EDITOR
	/** Autoregister abilities with the blueprint debugger in the editor.*/
	if (!HasAnyFlags(RF_ClassDefaultObject))
	{
		UBlueprint* BP = Cast<UBlueprint>(GetClass()->ClassGeneratedBy);
		if (BP && (BP->GetWorldBeingDebugged() == nullptr || BP->GetWorldBeingDebugged() == GetWorld()))
		{
			BP->SetObjectBeingDebugged(this);
		}
	}
#endif
}

UWorld* USmStepObject::GetWorld() const
{
	// 照GA抄的
	if (!IsInstantiated())
	{
		// If we are a CDO, we must return nullptr instead of calling Outer->GetWorld() to fool UObject::ImplementsGetWorld.
		return nullptr;
	}
	return GetOuter()->GetWorld();
}

int32 USmStepObject::GetFunctionCallspace(UFunction* Function, FFrame* Stack)
{
	// 照GA抄的，完全不懂，Callspace是个什么概念
	if (HasAnyFlags(RF_ClassDefaultObject) || !IsSupportedForNetworking())
	{
		// This handles absorbing authority/cosmetic
		return GEngine->GetGlobalFunctionCallspace(Function, this, Stack);
	}
	check(GetOuter() != nullptr);
	return GetOuter()->GetFunctionCallspace(Function, Stack);
}

bool USmStepObject::CallRemoteFunction(UFunction* Function, void* Parameters, FOutParmRec* OutParms, FFrame* Stack)
{
	// 照GA抄的，不懂，大概就是远程调用方法之类的
	check(!HasAnyFlags(RF_ClassDefaultObject));
	check(GetOuter() != nullptr);

	AActor* Owner = CastChecked<AActor>(GetOuter());

	bool bProcessed = false;

	FWorldContext* const Context = GEngine->GetWorldContextFromWorld(GetWorld());
	if (Context != nullptr)
	{
		for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
		{
			if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateFunction(Owner, Function))
			{
				Driver.NetDriver->ProcessRemoteFunction(Owner, Function, Parameters, OutParms, Stack, this);
				bProcessed = true;
			}
		}
	}

	return bProcessed;
}

bool USmStepObject::IsSupportedForNetworking() const
{
	// 照GA抄的
	/**
	 *	We can only replicate references to:
	 *		-CDOs and DataAssets (e.g., static, non-instanced gameplay abilities)
	 *		-Instanced abilities that are replicating (and will thus be created on clients).
	 *		
	 *	Otherwise it is not supported, and it will be recreated on the client
	 */

	bool Supported = GetReplicationPolicy() != ESmStepObjectReplicationPolicy::ReplicateNo || GetOuter()->IsA(UPackage::StaticClass());

	return Supported;
}

UGameplayTasksComponent* USmStepObject::GetGameplayTasksComponent(const UGameplayTask& Task) const
{
	if (GetCurrentActorInfo())
	{
		return  Cast<UGameplayTasksComponent>(GetCurrentActorInfo()->SmQuestComponent.Get());
	}

	return nullptr;
}

AActor* USmStepObject::GetGameplayTaskOwner(const UGameplayTask* Task) const
{
	const FSmStepObjectActorInfo* Info = GetCurrentActorInfo();
	return Info ? Info->OwnerActor.Get() : nullptr;
}

AActor* USmStepObject::GetGameplayTaskAvatar(const UGameplayTask* Task) const
{
	const FSmStepObjectActorInfo* Info = GetCurrentActorInfo();
	return Info ? Info->AvatarActor.Get() : nullptr;
}

void USmStepObject::OnGameplayTaskInitialized(UGameplayTask& Task)
{
	// todo 有自己的task类型时，对task进行初始化
	
	// UAbilityTask* AbilityTask = Cast<UAbilityTask>(&Task);
	// const FGameplayAbilityActorInfo* ActorInfo = GetCurrentActorInfo();
	//
	// if (AbilityTask && ActorInfo)
	// {
	// 	AbilityTask->SetAbilitySystemComponent(ActorInfo->AbilitySystemComponent.Get());
	// 	AbilityTask->Ability = this;
	// }
}

void USmStepObject::OnGameplayTaskActivated(UGameplayTask& Task)
{
	ActiveTasks.Add(&Task);
}

void USmStepObject::OnGameplayTaskDeactivated(UGameplayTask& Task)
{
	ActiveTasks.Remove(&Task);
}

bool USmStepObject::IsInstantiated() const
{
	return !HasAllFlags(RF_ClassDefaultObject);
}

bool USmStepObject::CanStepStart()
{
	return true;
}

void USmStepObject::StepStart()
{
	StepObjectState = ESmStepObjectState::Type::StepStart;
	
	if (bHasBlueprintStepStart)
	{
		K2_StepStart();
	}
	else
	{
		// Native child classes may want to override StepStart and do something like this:

		// Do stuff...
	}
}

void USmStepObject::PreStepStart()
{
}

void USmStepObject::CallStepStart()
{
	PreStepStart();
	StepStart();
}

void USmStepObject::K2_StepStartFinish()
{
	StepStartFinish();
}

void USmStepObject::StepStartFinish()
{
	StepObjectState = ESmStepObjectState::Type::StepInProcess;
}

void USmStepObject::CheckStepSuccessCondition()
{
	if (StepObjectState != ESmStepObjectState::Type::StepInProcess)
	{
		return;
	}

	if (StepSuccessCondition())
	{
		StepEnd();
	}
}

void USmStepObject::StepEnd()
{
	StepObjectState = ESmStepObjectState::Type::StepEnd;

	if (bHasBlueprintStepEnd)
	{
		K2_StepEnd();
	}
	else
	{
		// Native child classes may want to override StepEnd and do something like this:

		// Do stuff...
	}
}

void USmStepObject::K2_StepEndFinish()
{
	StepEndFinish();
}

void USmStepObject::StepEndFinish()
{
	// todo 这里还是改成直接调用ActorInfo中的Component的方法，不用绑定的方式去做（用委托去做就是最小知道原则，用ActorInfo去做就是统一的最大知道原则）
	OnStepEndFinish.ExecuteIfBound();
}
